#ifndef SD_RNG2_HPP
#define SD_RNG2_HPP

#include "config.hpp"
#include "linalg.hpp"

#include "boost/random.hpp"

namespace MT {

// RNG -- MT's abstract random number generator that generates a number from 0 to N-1
class RNG
{
protected: // internal data
    int N;

public: // public methods
    RNG(int N=1) : N(N) {}
    int getN() const { return N; }
    virtual inline int operator()() = 0;
};

class UniformRNG : public RNG
{
protected: // internal data
    boost::mt19937 rng;
    boost::uniform_smallint<> dist;
    boost::variate_generator<boost::mt19937, boost::uniform_smallint<> > die;
public: //public methods
    UniformRNG(int N=1) : RNG(N), rng(), dist(0, N-1), die(rng, dist) {}
    inline int operator()() { return die(); }
};


// PdfRNG -- a random number generator based on a distribution
template<typename _Scalar, int _Rows>
class PdfRNG : public RNG
{
protected: // internal data
    Eigen::Matrix<_Scalar, _Rows, 1> cdf;
    boost::mt19937 rng;
    boost::uniform_real<_Scalar> dist;
    boost::variate_generator<boost::mt19937, boost::uniform_real<_Scalar> > die;

public:
    PdfRNG(Eigen::Matrix<_Scalar, _Rows, 1> const &weights)
        : RNG(weights.rows()), rng(), dist(), die(rng, dist)
    {
        int2d(weights, cdf);
        cdf /= cdf[N-1];
    }

    inline int operator()()
    {
        return std::lower_bound(cdf.data(), cdf.data()+N, die()) - cdf.data();
    }
};


} // namespace MT

#endif
